<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="xB1HL" id="xB1HL"><span data-lake-id="uc10e6194" id="uc10e6194">典型回答</span></h1>
  <p data-lake-id="u12e8d0b0" id="u12e8d0b0"><br></p>
  <p data-lake-id="u6f9c2109" id="u6f9c2109"><span data-lake-id="ucfeadccd" id="ucfeadccd">对于单CPU的计算机来说，在任意时刻只能执行一条机器指令，每个线程只有获得CPU的使用权才能执行指令。</span></p>
  <p data-lake-id="u9104bafb" id="u9104bafb"><br></p>
  <p data-lake-id="ub7d45c45" id="ub7d45c45"><span data-lake-id="u1ffedfb8" id="u1ffedfb8">所谓多线程的并发运行，其实是指从宏观上看，各个线程轮流获得CPU的使用权，分别执行各自的任务。</span></p>
  <p data-lake-id="u52effeb8" id="u52effeb8"><br></p>
  <p data-lake-id="u9837f457" id="u9837f457"><span data-lake-id="u63c4652f" id="u63c4652f">前面关于线程状态的介绍中，我们知道，线程的运行状态中包含两种子状态，即就绪（READY）和运行中(RUNNING)。</span></p>
  <p data-lake-id="u8abc1405" id="u8abc1405"><br></p>
  <p data-lake-id="u80467aac" id="u80467aac"><span data-lake-id="u1a1a602d" id="u1a1a602d">而一个线程想要从就绪状态变成运行中状态，这个过程需要系统调度，即给线程分配CPU的使用权，获得CPU使用权的线程才会从就绪状态变成运行状态。</span></p>
  <p data-lake-id="u0f620378" id="u0f620378"><br></p>
  <p data-lake-id="u672cd2e1" id="u672cd2e1"><strong><span data-lake-id="ueff49003" id="ueff49003">给多个线程按照特定的机制分配CPU的使用权的过程就叫做线程调度。</span></strong></p>
  <p data-lake-id="u44e45d09" id="u44e45d09"><br></p>
  <p data-lake-id="ue4f3cb8a" id="ue4f3cb8a"><span data-lake-id="u20363cea" id="u20363cea">进程是分配资源的基本单元，线程是CPU调度的基本单元。这里所说的调度指的就是给其分配CPU时间片，让其执行任务。</span></p>
  <p data-lake-id="u1d214080" id="u1d214080"><br></p>
  <h3 data-lake-id="bXOSa" id="bXOSa"><span data-lake-id="u3b844c7f" id="u3b844c7f">Linux线程调度</span></h3>
  <p data-lake-id="ud9e00411" id="ud9e00411"><br></p>
  <p data-lake-id="u2275cece" id="u2275cece"><span data-lake-id="u2e5378d8" id="u2e5378d8">在Linux中，线程是由进程来实现，线程就是轻量级进程（&nbsp;lightweight&nbsp;process&nbsp;），因此在Linux中，线程的调度是按照进程的调度方式来进行调度的，也就是说线程是调度单元。</span></p>
  <p data-lake-id="u39720dc3" id="u39720dc3"><br></p>
  <p data-lake-id="u59d1c998" id="u59d1c998"><span data-lake-id="u636c730b" id="u636c730b">Linux这样实现的线程的好处的之一是：线程调度直接使用进程调度就可以了，没必要再搞一个进程内的线程调度器。在Linux中，调度器是基于线程的调度策略（scheduling&nbsp;policy）和静态调度优先级（static&nbsp;scheduling&nbsp;priority）来决定那个线程来运行。</span></p>
  <p data-lake-id="ubfcffc47" id="ubfcffc47"><br></p>
  <p data-lake-id="u8cffc90a" id="u8cffc90a"><span data-lake-id="udeb892cd" id="udeb892cd">在Linux中，主要有三种调度策略。分别是：</span></p>
  <p data-lake-id="u51199cea" id="u51199cea"><br></p>
  <ul list="ue3bf8e3f">
   <li fid="ub4a40539" data-lake-id="u480116cc" id="u480116cc"><span data-lake-id="u663b70f0" id="u663b70f0">SCHED_OTHER 分时调度策略，（默认的）</span></li>
   <li fid="ub4a40539" data-lake-id="u9c1a0e97" id="u9c1a0e97"><span data-lake-id="ucf984fae" id="ucf984fae">SCHED_FIFO 实时调度策略，先到先服务</span></li>
   <li fid="ub4a40539" data-lake-id="u4cd5d079" id="u4cd5d079"><span data-lake-id="ubc982070" id="ubc982070">SCHED_RR 实时调度策略，时间片轮转</span></li>
  </ul>
  <p data-lake-id="u4a21d54c" id="u4a21d54c"><br></p>
  <h3 data-lake-id="Ftj3H" id="Ftj3H"><span data-lake-id="uab2ae77e" id="uab2ae77e">Windows线程调度</span></h3>
  <p data-lake-id="u4cda5a6e" id="u4cda5a6e"><br></p>
  <p data-lake-id="u473ca777" id="u473ca777"><span data-lake-id="u59587628" id="u59587628">Windows 采用基于优先级的、抢占调度算法来调度线程。</span></p>
  <p data-lake-id="uf546bb5d" id="uf546bb5d"><br></p>
  <p data-lake-id="u47291c27" id="u47291c27"><span data-lake-id="u8154666f" id="u8154666f">用于处理调度的 Windows 内核部分称为调度程序，Windows 调度程序确保具有最高优先级的线程总是在运行的。由于调度程序选择运行的线程会一直运行，直到被更高优先级的线程所抢占，或终止，或时间片已到，或调用阻塞系统调用（如 I/O）。如果在低优先级线程运行时，更高优先级的实时线程变成就绪，那么低优先级线程就被抢占。这种抢占使得实时线程在需要使用 CPU 时优先得到使用。</span></p>
  <p data-lake-id="uc3a067fb" id="uc3a067fb"><br></p>
  <h3 data-lake-id="zhgk7" id="zhgk7"><span data-lake-id="u0b5cc74f" id="u0b5cc74f">Java线程调度</span></h3>
  <p data-lake-id="udbd09294" id="udbd09294"><br></p>
  <p data-lake-id="u612cfce8" id="u612cfce8"><span data-lake-id="u0f12556b" id="u0f12556b">可以看到，不同的操作系统，有不同的线程调度策略。但是，作为一个Java开发人员来说，我们日常开发过程中一般很少关注操作系统层面的东西。</span></p>
  <p data-lake-id="uf300db29" id="uf300db29"><br></p>
  <p data-lake-id="u924d5314" id="u924d5314"><span data-lake-id="u587ff066" id="u587ff066">主要是因为Java程序都是运行在Java虚拟机上面的，而虚拟机帮我们屏蔽了操作系统的差异，所以我们说Java是一个跨平台语言。</span></p>
  <p data-lake-id="uac1a0028" id="uac1a0028"><br></p>
  <p data-lake-id="uc80df044" id="uc80df044"><strong><span data-lake-id="u02853dc3" id="u02853dc3">在操作系统中，一个Java程序其实就是一个进程。所以，我们说Java是单进程、多线程的！</span></strong></p>
  <p data-lake-id="uc545bbfe" id="uc545bbfe"><br></p>
  <p data-lake-id="ue5caf824" id="ue5caf824"><span data-lake-id="u7b731124" id="u7b731124">前面关于线程的实现也介绍过，Thread类与大部分的Java API有显著的差别，它的所有关键方法都是声明为Native的，也就是说，他需要根据不同的操作系统有不同的实现。</span></p>
  <p data-lake-id="u15332c2a" id="u15332c2a"><br></p>
  <p data-lake-id="u1481f2e9" id="u1481f2e9"><span data-lake-id="ucd29ba1b" id="ucd29ba1b">在Java的多线程程序中，为保证所有线程的执行能按照一定的规则执行，JVM实现了一个线程调度器，它定义了线程调度模型，对于CPU运算的分配都进行了规定，按照这些特定的机制为多个线程分配CPU的使用权。</span></p>
  <p data-lake-id="uf06468ec" id="uf06468ec"><br></p>
  <p data-lake-id="uac6bace4" id="uac6bace4"><span data-lake-id="u4853c566" id="u4853c566">主要有两种调度模型：</span><strong><span data-lake-id="u3c96a566" id="u3c96a566">协同式线程调度</span></strong><span data-lake-id="ua5deb65c" id="ua5deb65c">和</span><strong><span data-lake-id="ua53667e2" id="ua53667e2">抢占式调度模型</span></strong><span data-lake-id="uca154d98" id="uca154d98">。</span></p>
  <p data-lake-id="ue05baa9c" id="ue05baa9c"><br></p>
  <h4 data-lake-id="sr3Jb" id="sr3Jb"><span data-lake-id="u3eedefb7" id="u3eedefb7">协同式线程调度</span></h4>
  <p data-lake-id="uacf61138" id="uacf61138"><br></p>
  <p data-lake-id="ua5137052" id="ua5137052"><span data-lake-id="ue39ec44f" id="ue39ec44f">协同式调度的多线程系统，线程的执行时间由线程本身来控制，线程把自己的工作执行完了之后，要主动通知系统切换到另外一个线程上。协同式多线程的最大好处是实现简单，而且由于线程要把自己的事情干完后才会进行线程切换，切换操作对线程自己是可知的，所以没有什么线程同步的问题。</span></p>
  <p data-lake-id="uccdb2b12" id="uccdb2b12"><br></p>
  <h4 data-lake-id="hGhak" id="hGhak"><span data-lake-id="u3db55e97" id="u3db55e97">抢占式调度模型</span></h4>
  <p data-lake-id="uc2a247c1" id="uc2a247c1"><br></p>
  <p data-lake-id="u4c20cabe" id="u4c20cabe"><span data-lake-id="ub196e386" id="ub196e386">抢占式调度的多线程系统，那么每个线程将由系统来分配执行时间，线程的切换不由线程本身来决定。在这种实现线程调度的方式下，线程的执行时间是系统可控的，也不会有一个线程导致整个进程阻塞的问题。</span></p>
  <p data-lake-id="uea440b4e" id="uea440b4e"><br></p>
  <p data-lake-id="uac19c2f8" id="uac19c2f8"><span data-lake-id="ucfe68741" id="ucfe68741">系统会让可运行池中优先级高的线程占用CPU，如果可运行池中的线程优先级相同，那么就随机选择一个线程，使其占用CPU。处于运行状态的线程会一直运行，直至它不得不放弃CPU。</span></p>
  <p data-lake-id="u8bd0ee2f" id="u8bd0ee2f"><br></p>
  <p data-lake-id="u0ac9fa3a" id="u0ac9fa3a"><strong><span data-lake-id="uf4788132" id="uf4788132">Java虚拟机采用抢占式调度模型。</span></strong></p>
  <p data-lake-id="ufe7e1fc0" id="ufe7e1fc0"><br></p>
  <p data-lake-id="u3cab1787" id="u3cab1787"><span data-lake-id="ucaad25fc" id="ucaad25fc">虽然Java线程调度是系统自动完成的，但是我们还是可以“建议”系统给某些线程多分配一点执行时间，另外的一些线程则可以少分配一点——这项操作可以通过设置线程优先级来完成。Java语言一共设置了10个级别的线程优先级（Thread.MIN_PRIORITY至Thread.MAX_PRIORITY），在两个线程同时处于Ready状态时，优先级越高的线程越容易被系统选择执行。</span></p>
  <p data-lake-id="ub4d31710" id="ub4d31710"><br></p>
  <p data-lake-id="u220f7e68" id="u220f7e68"><span data-lake-id="u9880d04c" id="u9880d04c">不过，线程优先级并不是太靠谱，原因是Java的线程是通过映射到系统的原生线程上来实现的，所以线程调度最终还是取决于操作系统，虽然现在很多操作系统都提供线程优先级的概念，但是并不见得能与Java线程的优先级一一对应。</span></p>
 </body>
</html>